package me.zhenqu.redis;

import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import java.io.*;
import java.util.*;

/**
 * create at: 11-4-20
 *
 * @author Yonghui Chen
 */
public class RedisOperator {
    private static Logger logger = Logger.getLogger(RedisOperator.class);

    public static final String REDIS_CONFIG = "redis.config";
    private MonitorJedisPool pool;

    private static final Map<String, RedisOperator> operators = new Hashtable<String, RedisOperator>();
    private String opKey;

    public static RedisOperator getInstance() {
        return getOperator("server");
    }

    public static RedisOperator getCacheInstance() {
        return getOperator("cache-server");
    }

    public static RedisOperator getOperator(String key) {
        if (operators.containsKey(key)) return operators.get(key);

        synchronized (operators) {
            if (operators.containsKey(key)) return operators.get(key);
            RedisOperator inst = new RedisOperator(key);
            operators.put(key, inst);
            return operators.get(key);
        }
    }

    public Jedis getJedis() {
        ThreadBindingRedisOperator op = ThreadBindingRedisOperator.getOperator();
        return (op == null) ? pool.getResource() : op.getJedis(opKey);
    }

    public Jedis getReadWriteJedis() {
        return pool.getReadWriteResource();
    }

    public Jedis getJedisFromPool() {
        return pool.getResource();
    }

    public void returnResource(Jedis resource) {
        if (ThreadBindingRedisOperator.getOperator() == null) {
            pool.returnResource(resource);
        }
    }

    public void returnBrokenResource(Jedis resource) {
        pool.returnBrokenResource(resource);
        resource.disconnect();
    }

    private String makeKey(String category, String key) {
        return category + "__" + key;
    }

    private RedisOperator(String key) {
        this.opKey = key;
        Properties setting = new Properties();
        InputStream in = getClass().getResourceAsStream("/"+ REDIS_CONFIG);
        if (in != null) {
            try {
                setting.load(in);
                in.close();
            } catch (IOException ignored) {

            }
        }

        if (in != null) {
            try {
                setting.load(in);
                in.close();
            } catch (IOException ignored) {

            }
        }
        String server = setting.getProperty(key);
        if (server == null) throw new IllegalArgumentException("redis.config error, " + key + " required");
        int port = Integer.parseInt(setting.getProperty(key + ".port", "6379"));
        int rwPort = Integer.parseInt(setting.getProperty(key + ".rw.port", "6379"));
        String rwServer = setting.getProperty(key + ".rw");
        logger.info(key + " using Redis Server: " + server + ":" + port);
        pool = new MonitorJedisPool(server, port, rwServer, rwPort);
    }

    public boolean set(String category, String key, String value) {
        return set(makeKey(category, key), value);
    }

    public boolean set(String category, String key, byte[] value, int expireSeconds) {
        return set(makeKey(category, key), value, expireSeconds);
    }

    public boolean set(String key, byte[] value, int expireSeconds) {
        Jedis resource = getReadWriteJedis();
        try {
            boolean ret = "OK".equals(resource.setex(toBytes(key),
                    expireSeconds,
                    value));
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
    }

    public Map<String, byte[]> multiGetBytes(String category, List<String> keys) {
        Jedis resource = getJedis();
        try {
            String[] strings = makeKey(category, keys.toArray(new String[keys.size()]));
            Map<String, byte[]> result = new HashMap<String, byte[]>(keys.size());
            byte[][] byteKeys = new byte[keys.size()][];
            int i = 0;
            for (String key : strings) {
                byteKeys[i++] = toBytes(key);
            }
            List<byte[]> ret = resource.mget(byteKeys);

            returnResource(resource);

            for (int j = 0; j < keys.size(); j++) {
                byte[] value = ret.get(j);
                if (value != null) {
                    result.put(keys.get(j), value);
                }
            }
            return result;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
/*
        if (keys.size() > 10) {
            Map<String, byte[]> result = new HashMap<String, byte[]>(keys.size());
            for (int i = 0; i < keys.size() / 10 + 1; i++) {
                int from = i * 10;
                int to = (i + 1) * 10;
                if (to > keys.size()) {
                    to = keys.size();
                }
                result.putAll(get(category, keys.subList(from, to)));
            }
            return result;
        } else {
            return get(category, keys);
        }
*/
    }

    private Map<String, byte[]> get(String category, List<String> keys) {
        Jedis resource = getJedis();
        try {
            String[] strings = makeKey(category, keys.toArray(new String[keys.size()]));
            Map<String, byte[]> result = new HashMap<String, byte[]>(keys.size());
            List<byte[]> ret = new ArrayList<byte[]>();
            switch (keys.size()) {
                case 3:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]), toBytes(strings[2]));
                    break;
                case 4:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]),
                            toBytes(strings[2]), toBytes(strings[3]));
                    break;
                case 5:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]),
                            toBytes(strings[2]), toBytes(strings[3]),
                            toBytes(strings[4]));
                    break;
                case 6:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]),
                            toBytes(strings[2]), toBytes(strings[3]),
                            toBytes(strings[4]), toBytes(strings[5]));
                    break;
                case 7:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]),
                            toBytes(strings[2]), toBytes(strings[3]),
                            toBytes(strings[4]), toBytes(strings[5]),
                            toBytes(strings[6]));
                    break;
                case 10:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]),
                            toBytes(strings[2]), toBytes(strings[3]),
                            toBytes(strings[4]), toBytes(strings[5]),
                            toBytes(strings[6]), toBytes(strings[7]),
                            toBytes(strings[8]), toBytes(strings[9]));
                    break;
                case 9:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]),
                            toBytes(strings[2]), toBytes(strings[3]),
                            toBytes(strings[4]), toBytes(strings[5]),
                            toBytes(strings[6]), toBytes(strings[7]),
                            toBytes(strings[8]));
                    break;
                case 8:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]),
                            toBytes(strings[2]), toBytes(strings[3]),
                            toBytes(strings[4]), toBytes(strings[5]),
                            toBytes(strings[6]), toBytes(strings[7]));
                    break;
                case 2:
                    ret = resource.mget(toBytes(strings[0]), toBytes(strings[1]));
                    break;
                case 1:
                    ret = resource.mget(toBytes(strings[0]));
                    break;
                case 0:
            }
            returnResource(resource);

            for (int j = 0; j < keys.size(); j++) {
                byte[] value = ret.get(j);
                if (value != null) {
                    result.put(keys.get(j), value);
                }
            }
            return result;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
    }

    public Map<String, byte[]> multiGetBytes(List<String> keys) {
        Jedis resource = getJedis();
        try {
            byte[][] ks = new byte[keys.size()][];
            int i = 0;
            for (String key : keys) {
                ks[i++] = toBytes(key);
            }
            List<byte[]> ret = resource.mget(ks);
            returnResource(resource);
            Map<String, byte[]> result = new HashMap<String, byte[]>(keys.size());
            for (int j = 0; j < keys.size(); j++) {
                byte[] value = ret.get(j);
                if (value != null) {
                    result.put(keys.get(j), value);
                }
            }
            return result;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
    }

    public void mset(String category, Map<String, byte[]> values) {
        if (values == null || values.isEmpty()) return;
        Map<String, byte[]> targets = new HashMap<String, byte[]>(values.size());
        for (Map.Entry<String, byte[]> entry : values.entrySet()) {
            targets.put(makeKey(category, entry.getKey()), entry.getValue());
        }

        mset(targets);
    }

    public void mset(Map<String, byte[]> values) {
        if (values == null || values.isEmpty()) return;
        byte[][] params = new byte[values.size() * 2][];
        int i = 0;
        for (Map.Entry<String, byte[]> entry : values.entrySet()) {
            params[i++] = toBytes(entry.getKey());
            params[i++] = entry.getValue();
        }
        Jedis resource = getReadWriteJedis();
        try {
            resource.mset(params);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
    }

    public void mset(Map<String, byte[]> values, int expireSeconds) {
        mset(values);
        for (String s : values.keySet()) {
            setExpire(s, expireSeconds);
        }
    }

    public void mset(String category, Map<String, byte[]> values, int expireSeconds) {
        Map<String, byte[]> targets = new HashMap<String, byte[]>(values.size());
        for (Map.Entry<String, byte[]> entry : values.entrySet()) {
            targets.put(makeKey(category, entry.getKey()), entry.getValue());
        }

        mset(targets, expireSeconds);
    }

    private byte[] toBytes(String key) {
        try {
            return key.getBytes("utf-8");
        } catch (UnsupportedEncodingException e) {
            return key.getBytes();
        }
    }

    public boolean set(String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            boolean ret = "OK".equals(resource.set(key, value));
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
    }

    public byte[] getBytes(String category, String key) {
        return getBytes(makeKey(category, key));
    }

    public byte[] getBytes(String key) {
        Jedis resource = getJedis();
        try {
            byte[] ret = resource.get(toBytes(key));
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
    }

    public boolean set(String category, String key, byte[] value) {
        return set(makeKey(category, key), value);
    }

    public boolean set(String key, byte[] value) {
        Jedis resource = getReadWriteJedis();
        try {
            String ret = resource.set(toBytes(key), value);
            returnResource(resource);
            return "OK".equals(ret);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);
            throw e;
        }
    }


    private String[] makeKey(String category, String[] key) {
        String[] keys = new String[key.length];
        int i = 0;
        for (String s : key) {
            keys[i++] = makeKey(category, s);
        }
        return keys;
    }

    public long increase(String category, String key, int inc) {
        return increase(makeKey(category, key), inc);
    }

    public long increase(String key, int inc) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.incrBy(key, inc);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }


    public long decrease(String category, String key, int inc) {
        return decrease(makeKey(category, key), inc);
    }

    public long decrease(String key, int inc) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.decrBy(key, inc);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String getString(String category, String key) {
        return getString(makeKey(category, key));
    }

    public Long getLong(String category, String key) {
        return getLong(makeKey(category, key));
    }

    public boolean getBoolean(String category, String key) {
        return getBoolean(makeKey(category, key));
    }

    public String getString(String key) {
        Jedis resource = getJedis();
        try {
            String ret = resource.get(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean getBoolean(String key) {
        Jedis resource = getJedis();
        try {
            boolean ret = "1".equals(resource.get(key));
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Long getLong(String key) {
        Jedis resource = getJedis();
        try {
            String s = resource.get(key);
            Long ret = s == null ? null : Long.parseLong(s);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean exists(String key) {
        Jedis resource = getJedis();
        try {
            Boolean ret = resource.exists(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean exists(String category, String key) {
        return exists(makeKey(category, key));
    }

    public void removeCategory(String category, String... key) {
        Jedis resource = getReadWriteJedis();
        try {
            String[] keys = new String[key.length];
            int i = 0;
            for (String s : key) {
                keys[i++] = makeKey(category, s);
            }
            resource.del(keys);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void remove(String... key) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.del(key);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> keys(final String pattern) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.keys(pattern);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long setExpire(String key, int second) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.expire(key, second);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long setExpire(String category, String key, int second) {
        return setExpire(makeKey(category, key), second);
    }

    public long setExpireAt(String key, long unixTimeSecond) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.expireAt(key, unixTimeSecond);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long setExpireAt(String category, String key, long unixTimeSecond) {
        return setExpireAt(makeKey(category, key), unixTimeSecond);
    }

    public List<String> multiGet(String... keys) {
        Jedis resource = getJedis();
        try {
            List<String> ret = resource.mget(keys);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public List<String> multiGet(String category, String... key) {
        if (key.length == 0) return Collections.emptyList();
        Jedis resource = getJedis();
        try {
            String[] keys = new String[key.length];
            int i = 0;
            for (String s : key) {
                keys[i++] = makeKey(category, s);
            }
            List<String> ret = resource.mget(keys);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public List<String> multiGet(String category, List<String> key) {
        if (key == null || key.isEmpty()) return Collections.emptyList();
        Jedis resource = getJedis();
        try {
            String[] keys = new String[key.size()];
            int i = 0;
            for (String s : key) {
                keys[i++] = makeKey(category, s);
            }
            List<String> ret = resource.mget(keys);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean setIfNotExists(String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            boolean ret = resource.setnx(key, value) == 1;
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean setIfNotExists(String category, String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            boolean ret = resource.setnx(makeKey(category, key), value) == 1;
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void set(Map<String, String> keyValues) {
        Jedis resource = getReadWriteJedis();
        try {
            String[] kvs = new String[keyValues.size() * 2];
            int i = 0;
            for (Map.Entry<String, String> entry : keyValues.entrySet()) {
                kvs[i++] = entry.getKey();
                kvs[i++] = entry.getValue();
            }
            resource.mset(kvs);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void set(String category, Map<String, String> keyValues) {
        Jedis resource = getReadWriteJedis();
        try {
            String[] kvs = new String[keyValues.size() * 2];
            int i = 0;
            for (Map.Entry<String, String> entry : keyValues.entrySet()) {
                kvs[i++] = makeKey(category, entry.getKey());
                kvs[i++] = entry.getValue();
            }
            resource.mset(kvs);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void setEx(String key, String value, int ttlSecond) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.setex(key, ttlSecond, value);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void setEx(String category, String key, String value, int ttlSecond) {
        setEx(makeKey(category, key), value, ttlSecond);
    }

    public void append(String category, String key, String value) {
        append(makeKey(category, key), value);
    }

    public void append(String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.append(key, value);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String substr(String key, int begin, int end) {
        Jedis resource = getJedis();
        try {
            String ret = resource.substr(key, begin, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String substr(String category, String key, int begin, int end) {
        return substr(makeKey(category, key), begin, end);
    }

    public void setField(String key, String field, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.hset(key, field, value);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void setField(String category, String key, String field, String value) {
        setField(makeKey(category, key), field, value);
    }

    public void setFields(String key, Map<String, String> fields) {
        if (fields.isEmpty()) return;
        Jedis resource = getReadWriteJedis();
        try {
            resource.hmset(key, fields);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void setFields(String category, String key, Map<String, String> fields) {
        setFields(makeKey(category, key), fields);
    }

    public List<String> getFields(String category, String key, String... fields) {
        return getFields(makeKey(category, key), fields);
    }

    public List<String> getFields(String key, String... fields) {
        Jedis resource = getJedis();
        try {
            List<String> ret = resource.hmget(key, fields);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long increaseField(String key, String field, long inc) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.hincrBy(key, field, inc);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long increaseField(String category, String key, String field, long inc) {
        return increaseField(makeKey(category, key), field, inc);
    }

    public boolean fieldExists(String category, String key, String field) {
        return fieldExists(makeKey(category, key), field);
    }

    public boolean fieldExists(String key, String field) {
        Jedis resource = getJedis();
        try {
            Boolean ret = resource.hexists(key, field);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean deleteField(String category, String key, String field) {
        return deleteField(makeKey(category, key), field);
    }

    public boolean deleteField(String key, String field) {
        Jedis resource = getReadWriteJedis();
        try {
            boolean ret = resource.hdel(key, field) == 1;
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long fieldSize(String key) {
        Jedis resource = getJedis();
        try {
            Long ret = resource.hlen(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long fieldSize(String category, String key) {
        return fieldSize(makeKey(category, key));
    }

    public Set<String> fieldNames(String category, String key) {
        return fieldNames(makeKey(category, key));
    }

    public Set<String> fieldNames(String key) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.hkeys(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public List<String> getFieldValues(String key) {
        Jedis resource = getJedis();
        try {
            List<String> ret = resource.hvals(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public List<String> getFieldValues(String category, String key) {
        return getFieldValues(makeKey(category, key));
    }

    public Map<String, String> getAllFieldValues(String key) {
        Jedis resource = getJedis();
        try {
            Map<String, String> ret = resource.hgetAll(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }


    public Map<String, String> getAllFieldValues(String category, String key) {
        return getAllFieldValues(makeKey(category, key));
    }

    public String getField(String key, String field) {
        Jedis resource = getJedis();
        try {
            String ret = resource.hget(key, field);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String getField(String category, String key, String field) {
        return getField(makeKey(category, key), field);
    }

// -----------list

    public boolean appendToList(String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            boolean ret = resource.rpush(key, value) == 1;
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean appendToList(String category, String key, String value) {
        return appendToList(makeKey(category, key), value);
    }

    public boolean prependToList(String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            boolean ret = resource.lpush(key, value) == 1;
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long size(String key) {
        Jedis resource = getJedis();
        try {
            Long ret = resource.llen(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long size(String category, String key) {
        return size(makeKey(category, key));
    }

    public List<String> subList(String key, int begin, int end) {
        Jedis resource = getJedis();
        try {
            List<String> ret = resource.lrange(key, begin, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public List<String> subList(String category, String key, int begin, int end) {
        return subList(makeKey(category, key), begin, end);
    }

    public String trimList(String key, int begin, int end) {
        Jedis resource = getReadWriteJedis();
        try {
            String ret = resource.ltrim(key, begin, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String trimList(String category, String key, int begin, int end) {
        return trimList(makeKey(category, key), begin, end);
    }

    public String get(String category, String key, int index) {
        return get(makeKey(category, key), index);
    }

    public String get(String key, int index) {
        Jedis resource = getJedis();
        try {
            String ret = resource.lindex(key, index);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String pop(String category, String key) {
        return pop(makeKey(category, key));
    }

    public String pop(String key) {
        Jedis resource = getReadWriteJedis();
        try {
            String ret = resource.lpop(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String tail(String category, String key) {
        return tail(makeKey(category, key));
    }

    public String tail(String key) {
        Jedis resource = getReadWriteJedis();
        try {
            String ret = resource.rpop(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    /**
     * 从src pop，插入到dest中
     *
     * @param srcKey
     * @param destKey
     */
    public void moveTo(String srcKey, String destKey) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.rpoplpush(srcKey, destKey);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void moveTo(String category, String srcKey, String destKey) {
        moveTo(makeKey(category, srcKey), makeKey(category, destKey));
    }

    public String set(String category, String key, int index, String value) {
        return set(makeKey(category, key), index, value);
    }

    public String set(String key, int index, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            String ret = resource.lset(key, index, value);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long remove(String category, String key, int count, String value) {
        return remove(makeKey(category, key), count, value);
    }

    public long remove(String key, int count, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.lrem(key, count, value);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public boolean prependToList(String category, String key, String value) {
        return prependToList(makeKey(category, key), value);
    }

    // --- set
    public void add(String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.sadd(key, value);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void add(String category, String key, String value) {
        add(makeKey(category, key), value);
    }

    public Set<String> getSet(String key) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.smembers(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> getSet(String category, String key) {
        return getSet(makeKey(category, key));
    }

    public void removeValue(String category, String key, String value) {
        removeValue(makeKey(category, key), value);
    }

    public void removeValue(String key, String value) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.srem(key, value);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String popFromSet(String category, String key) {
        return popFromSet(makeKey(category, key));
    }

    public String popFromSet(String key) {
        Jedis resource = getReadWriteJedis();
        try {
            String ret = resource.spop(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void move(String srcKey, String destKey, String member) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.smove(srcKey, destKey, member);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void move(String category, String srcKey, String destKey, String member) {
        move(makeKey(category, srcKey), makeKey(category, destKey), member);
    }

    public long getSetSize(String key) {
        Jedis resource = getJedis();
        try {
            Long ret = resource.scard(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long getSetSize(String category, String key) {
        return getSetSize(makeKey(category, key));
    }

    public boolean contains(String category, String key, String val) {
        return contains(makeKey(category, key), val);
    }

    public boolean contains(String key, String val) {
        Jedis resource = getJedis();
        try {
            Boolean ret = resource.sismember(key, val);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }
    //public Set<String> sinter(final String... keys) {??
    //public Long sinterstore(final String dstkey, final String... keys) { ??

    public Set<String> union(String... keys) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.sunion(keys);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> union(String category, String... keys) {
        return union(makeKey(category, keys));
    }

    public void unionStore(String destKey, String... keys) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.sunionstore(destKey, keys);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void unionStore(String category, String destKey, String... keys) {
        unionStore(makeKey(category, destKey), makeKey(category, keys));
    }

    public Set<String> diff(String category, String... keys) {
        return diff(makeKey(category, keys));
    }

    public Set<String> diff(String... keys) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.sdiff(keys);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void diffStore(String category, String destKey, String... keys) {
        diffStore(makeKey(category, destKey), makeKey(category, keys));
    }

    public void diffStore(String destKey, String... keys) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.sdiffstore(destKey, keys);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String randomGet(String key) {
        Jedis resource = getJedis();
        try {
            String ret = resource.srandmember(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public String randomGet(String category, String key) {
        return randomGet(makeKey(category, key));
    }


    ///zzz...
    public void add(String key, double score, String val) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.zadd(key, score, val);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public void add(String category, String key, double score, String val) {
        add(makeKey(category, key), score, val);
    }

    public double increaseScore(String key, double score, String val) {
        Jedis resource = getReadWriteJedis();
        try {
            Double ret = resource.zincrby(key, score, val);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public double increaseScore(String category, String key, double score, String val) {
        return increaseScore(makeKey(category, key), score, val);
    }

    public void rangeRemove(String category, String key, String val) {
        rangeRemove(makeKey(category, key), val);
    }

    public void rangeRemove(String key, String val) {
        Jedis resource = getReadWriteJedis();
        try {
            resource.zrem(key, val);
            returnResource(resource);
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long indexOf(String category, String key, String val) {
        return indexOf(makeKey(category, key), val);
    }

    public long indexOf(String key, String val) {
        Jedis resource = getJedis();
        try {
            Long ret = resource.zrank(key, val);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long lastIndexOf(String category, String key, String val) {
        return lastIndexOf(makeKey(category, key), val);
    }

    public long lastIndexOf(String key, String val) {
        Jedis resource = getJedis();
        try {
            Long ret = resource.zrevrank(key, val);
            returnResource(resource);
            return ret == null ? -1 : ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> range(final String category, final String key, final int start, final int end) {
        return range(makeKey(category, key), start, end);
    }

    public Set<String> range(final String key, final int start, final int end) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.zrange(key, start, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> reverse(final String category, final String key, final int start, final int end) {
        return reverse(makeKey(category, key), start, end);
    }

    public Set<String> reverse(final String key, final int start, final int end) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.zrevrange(key, start, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<Tuple> rangeWithScores(final String key, final int start,
                                      final int end) {
        Jedis resource = getJedis();
        try {
            Set<Tuple> ret = resource.zrangeWithScores(key, start, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<Tuple> rangeWithScores(final String category, final String key, final int start, final int end) {
        return rangeWithScores(makeKey(category, key), start, end);
    }

    public Set<Tuple> reverseWithScores(final String key, final int start,
                                        final int end) {
        Jedis resource = getJedis();
        try {
            Set<Tuple> ret = resource.zrevrangeWithScores(key, start, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<Tuple> reverseWithScores(final String category, final String key, final int start, final int end) {
        return reverseWithScores(makeKey(category, key), start, end);
    }

    public long sizeOfSet(String category, String key) {
        return sizeOfSet(makeKey(category, key));
    }

    public long sizeOfSet(String key) {
        Jedis resource = getJedis();
        try {
            Long ret = resource.zcard(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public double getScore(String category, String key, String val) {
        return getScore(makeKey(category, key), val);
    }

    public double getScore(String key, String val) {
        Jedis resource = getJedis();
        try {
            Double ret = resource.zscore(key, val);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long count(String category, String key, double min, double max) {
        return count(makeKey(category, key), min, max);
    }

    public long count(String key, double min, double max) {
        Jedis resource = getJedis();
        try {
            Long ret = resource.zcount(key, min, max);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> range(String category, String key, double min, double max) {
        return range(makeKey(category, key), min, max);
    }

    public Set<String> range(String key, double min, double max) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.zrangeByScore(key, min, max);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> range(String category, final String key, final double min,
                             final double max, final int offset, final int count) {
        return range(makeKey(category, key), min, max, offset, count);
    }

    public Set<String> range(final String key, final double min,
                             final double max, final int offset, final int count) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.zrangeByScore(key, min, max, offset, count);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> reverseRange(String category, final String key, final double min,
                                    final double max, final int offset, final int count) {
        return reverseRange(makeKey(category, key), min, max, offset, count);
    }

    public Set<Tuple> reverseRangeWithScore(String category, final String key, final double min,
                                            final double max, final int offset, final int count) {
        return reverseRangeWithScore(makeKey(category, key), min, max, offset, count);
    }

    public Set<Tuple> reverseRangeWithScore(final String key, final double min,
                                            final double max, final int offset, final int count) {
        Jedis resource = getJedis();
        try {
            Set<Tuple> ret = resource.zrevrangeByScoreWithScores(key, min, max, offset, count);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<String> reverseRange(final String key, final double min,
                                    final double max, final int offset, final int count) {
        Jedis resource = getJedis();
        try {
            Set<String> ret = resource.zrevrangeByScore(key, min, max, offset, count);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<Tuple> rangeByScoreWithScores(String category, final String key, final double min,
                                             final double max, final int offset, final int count) {
        return rangeByScoreWithScores(makeKey(category, key), min, max, offset, count);
    }

    public Set<Tuple> rangeByScoreWithScores(final String key, final double min,
                                             final double max, final int offset, final int count) {
        Jedis resource = getJedis();
        try {
            Set<Tuple> ret = resource.zrangeByScoreWithScores(key, min, max, offset, count);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public Set<Tuple> rangeByScoreWithScores(String category, final String key, final double min,
                                             final double max) {
        return rangeByScoreWithScores(makeKey(category, key), min, max);
    }

    public Set<Tuple> rangeByScoreWithScores(final String key, final double min,
                                             final double max) {
        Jedis resource = getJedis();
        try {
            Set<Tuple> ret = resource.zrangeByScoreWithScores(key, min, max);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long removeByRank(String category, final String key, final int start, final int end) {
        Jedis resource = getReadWriteJedis();
        try {
            Long aLong = resource.zremrangeByRank(makeKey(category, key), start, end);
            returnResource(resource);
            return aLong;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long removeByRank(final String key, final int start, final int end) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.zremrangeByRank(key, start, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long removeByScore(String category, final String key, final double start, final double end) {
        Jedis resource = getReadWriteJedis();
        try {
            Long aLong = resource.zremrangeByScore(makeKey(category, key), start, end);
            returnResource(resource);
            return aLong;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    public long removeByScore(final String key, final double start, final double end) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.zremrangeByScore(key, start, end);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }

    // 去除过期
    public long persist(final String key) {
        Jedis resource = getReadWriteJedis();
        try {
            Long ret = resource.persist(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }
    //zunion.....

    //sort         more...
    public List<String> sort(String category, final String key) {
        return sort(makeKey(category, key));
    }

    public List<String> sort(final String key) {
        Jedis resource = getJedis();
        try {
            List<String> ret = resource.sort(key);
            returnResource(resource);
            return ret;
        } catch (RuntimeException e) {
            returnBrokenResource(resource);

            throw e;
        }
    }
    //watch.......

    public void freeResource(Jedis resource) {
        pool.returnResource(resource);
    }
}
